/**
 * 工厂模式：调用即可返回新对象的函数
 *   Vue3-createApp
 * 1.避免在测试期间，全局配置污染其它测试用例 
 * 2.全局改变 Vue 实例的行为，移动到 Vue 实例上
 *   Axios-creatre
 * 1. 基于自定义配置新建实例【例如项目中设置不同的请求配置】
 */

/**
 * 单例模式：单例对象保证整个系统多次调用只有一个存在
 */

class SingleTon {
    static #instance
    static getInsance () {
        if (!this.#instance) {
            this.#instance = new SingleTon()
        }
        return this.#instance
    }
}
const s1 = SingleTon.getInsance()
const s2 = SingleTon.getInsance()
console.log(s1 === s2, s1, s2)

/**
 * 观察者模式：对象之间定义 一对多 的依赖，当一个对象状态改变的时候，所有依赖对象都会自动收到通知
 * 目标对象：一个
 * 观察者：一个或多个
 * 
 */


/**
 * 发布订阅模式：类似观察这模式 区别【一个有中间商（发布订阅模式），一个没有中间商（观察者模式）】
 */
Function.prototype.mybind = function (that, ...rest) {
    const _self = this
    return function (...arge) {
        const key = Symbol('key')
        that[key] = _self
        const res = that[key](...rest, ...arge)
        delete that[key]
        return res
    }
}

class EventBus {
    static #instance
    #eventMap = new Map()
    static getInsance () {
        if (!this.#instance) {
            this.#instance = new EventBus()
        }
        return this.#instance
    }
    $on (eventName, callBack) {
        if (eventName) {
            if (!this.#eventMap.get(eventName)) {
                this.#eventMap.set(eventName, [])
                this.#eventMap.get(eventName).push(callBack)
            } else {
                this.#eventMap.get(eventName).push(callBack)
            }
        }
    }
    $emit (eventName, ...rest) {
        if (eventName) {
            if (!this.#eventMap.get(eventName)) return
            this.#eventMap.get(eventName).forEach(item => {
                item(...rest)
            })
        }
    }
    $off (eventName) {
        if (eventName) {
            this.#eventMap.delete(eventName)
        }
    }
    $once (eventName, callBack) {
        if (!this.#eventMap.get(eventName)) {
            this.#eventMap.set(eventName, [])
            this.#eventMap.get(eventName).push((...rest) => {
                callBack(...rest)
                this.$off(eventName)
            })
        } else {
            const index = this.#eventMap.get(eventName).length - 1
            this.#eventMap.get(eventName).push((...rest) => {
                callBack(...rest)
                this.#eventMap.get(eventName).splice(index, 1)
            })
        }
    }
    log () {
        console.log(this.#eventMap)
    }
}

// EventBus.getInsance().$on('ccc', (...rest) => {
//     console.log(rest)
// })
// EventBus.getInsance().$on('ccc', (...rest) => {
//     console.log(rest, 123)
// })
// EventBus.getInsance().$on('aaa', (...rest) => {
//     console.log(rest, 'aaa')
// })
// EventBus.getInsance().$once('ccc', (...rest) => {
//     console.log(rest, 'once')
// })
// EventBus.getInsance().$on('ccc', (...rest) => {
//     console.log(rest, 'once000')
// })
// EventBus.getInsance().$once('aaa', (...rest) => {
//     console.log(rest, 'bbb')
// })
// EventBus.getInsance().$on('bbb', (...rest) => {
//     console.log(rest, 'bbb')
// })
// EventBus.getInsance().$off('bbb')
// EventBus.getInsance().log()
// EventBus.getInsance().$emit('aaa', 1, 2, 3, 4, 56, 9)
// EventBus.getInsance().$emit('aaa0000', 1, 2, 3, 4, 56, 9)
// // EventBus.getInsance().$emit('ccc', 999, 999, 999)
// EventBus.getInsance().log()


/**
 * 原型模式：通过复制一个已经存在的实例返回新的实例，而不是新建实例
 * 1. Object.create
 */


const food = {
    name: 'food',
    eat () {
        console.log('csavdv')
    }
}
const newFood = Object.create(food)
food.age = 18
console.log(food, newFood)


/**
 * 代理模式：为一个对象提供一个占位符或者代用品，以便控制对它的访问
 * 缓存代理：第一次查询的数据通过接口获取，重复查询通过缓存获取
 */
function cache (callBack) {
    const data = {}

    async function searchCity (params) {
        if (data[params]) return callBack(data[params])
        const res = await axios('http://hmajax.itheima.net/api/city', {
            params: {
                pname: params
            }
        })
        data[params] = res.data.list
        callBack(data[params])
        return res
    }
    return function createSearchInput (params) {
        const ipt = document.createElement('input')
        ipt.addEventListener('blur', async e => {
            const res = await searchCity(e.target.value)
        })
        document.body.appendChild(ipt)
    }
}
cache(getCity)()
function getCity (data) {
    console.log(data)
}


/**
 *迭代器模式：让用户透过特定接口巡防容器内的每一个元素而不用了解底层实现（遍历）
 * for in 和 for of 
 * 迭代器协议
 */

Object.prototype.objFunc = function () {

}
Array.prototype.arrFunc = function () {

}
const list = ['苹果派', '西瓜', '香蕉', '胡萝卜']

/** 
 * for in 语句可以遍历一个对象的除【symbol】以外的【可枚举】属性，包括集成的可枚举属性
 * 1. 遍历的是索引
 * 2. 继承而来无法遍历
 * */
for (const key in list) {
    console.log(key)
}
/** 
 * for of 语句可以遍历可迭代对象
 * 包括 Array、Map、Set、TypedArray、arguments 对象等
 * 1. 遍历的是索引
 * 2. 继承而来的也可以遍历出来（原型上动态增加的也可以遍历）
 * */
for (const iterator of list) {
    console.log('iterator', iterator)
}
const obj = {
    name: '123'
}
// for (const iterator of obj) {
//     console.log(iterator)
// }

/**
 * 迭代协议
 * 可迭代协议：增加方法 [Symbol.iterator](){} 返回符合 迭代器协议 的对象
 * 迭代器协议：有next方法的对象，next方法返回：
 *      已结束：{done:true}
 *      继续迭代：{done:false,value:'**'}
 *   1. 使用 Generator
 *   2. 自己实现对象-  next
 */
const data = {
    // Symbol.iterator 内置常量
    // [属性名表达式]
    [Symbol.iterator] () {
        // ============ 使用 Generator =============
        // function* fnGenerator () {
        //     yield '西蓝花'
        //     yield '胡萝卜'
        //     yield '炒鸡蛋'
        // }
        // const food = fnGenerator()
        // return food

        //======== 自己实现
        const list = ['北京', '上海', '广州', '深圳']
        let index = 0
        return {
            next () {
                if (index < list.length) {
                    return {
                        done: false,
                        value: list[index++]
                    }
                }
                return {
                    done: true
                }
            }
        }
    }
}
for (const iterator of data) {
    console.log('data', iterator)
}